7.3.1 Introducción a SQLite
A menudo, los ficheros de texto o JSON se quedan cortos. Cuando necesitamos integridad, relaciones complejas entre datos y una búsqueda eficiente, entramos en el mundo de las Bases de Datos Relacionales (RDBMS).
SQLite es una base de datos extraordinaria: no necesita un servidor (es serverless), vive en un simple archivo y es el motor de base de datos más desplegado del mundo (está en tu móvil, en tu navegador y en casi cualquier software moderno).
1️⃣ El Concepto: Base de Datos Embebida
A diferencia de MySQL o PostgreSQL, donde Python se conecta a un "servidor" externo, en SQLite Python es el motor de la base de datos.
- Todo en un archivo: Tu base de datos es un único fichero
.dbo.sqlite. - Zero Config: No hay que instalar servicios ni configurar puertos.
- Librería estándar: Python ya viene con el módulo
sqlite3de serie.
SQLite es perfecto para aplicaciones locales, herramientas de análisis y sitios web de tráfico medio. Para sistemas distribuidos masivos con cientos de escrituras simultáneas por segundo, se suelen preferir sistemas cliente-servidor.
2️⃣ El Ciclo de Vida de la Conexión
Para trabajar con SQLite, seguimos un flujo riguroso: Conectar ➡️ Operar ➡️ Confirmar ➡️ Cerrar.
🟩 Apertura de la conexión
La función sqlite3.connect() es nuestra puerta de entrada.
import sqlite3
# Crea el archivo si no existe
conexion = sqlite3.connect("mi_base_datos.db")
print("Conexión establecida con éxito")
# Siempre cerrar el recurso
conexion.close()
🟦 Bases de datos "efímeras" (en RAM)
A veces solo necesitas una base de datos para pruebas rápidas o procesamiento temporal que no quieres guardar en disco.
# Se crea en la memoria RAM y desaparece al cerrar el programa
conexion = sqlite3.connect(":memory:")
🟨 El Cursor: tu espacio de trabajo
La conexión es el "cable", pero el cursor es la "mano" que ejecuta las órdenes SQL.
cursor = conexion.cursor()
3️⃣ Creación de Estructuras (DDL)
Antes de guardar datos, definimos la "forma" de nuestra información mediante tablas.
import sqlite3
with sqlite3.connect("inventario.db") as conn:
cursor = conn.cursor()
# Usamos triples comillas para SQL multilínea (más legible)
cursor.execute("""
CREATE TABLE IF NOT EXISTS productos (
id INTEGER PRIMARY KEY AUTOINCREMENT,
nombre TEXT NOT NULL,
precio REAL CHECK(precio >= 0),
disponible BOOLEAN DEFAULT 1
)
""")
# En operaciones DDL (CREATE), no suele ser obligatorio commit,
# pero es una buena práctica para asegurar persistencia inmediata.
conn.commit()
SQLite usa tipado dinámico, pero reconoce estos tipos principales:
NULL: Valor nulo.INTEGER: Números enteros.REAL: Números de punto flotante.TEXT: Cadenas de caracteres (UTF-8).BLOB: Datos binarios (imágenes, archivos).
4️⃣ Gestión de Transacciones: Atomicidad
Este es el concepto más importante de las bases de datos. Una operación debe ser atómica: o se hace entera, o no se hace nada.
🟩 commit() — El punto de no retorno
Cuando insertas o modificas datos, los cambios se quedan en una "sala de espera" (buffer de transacción). commit() es el botón que los graba definitivamente en el disco.
🟦 with y las transacciones
En sqlite3, el uso de with sobre el objeto conexión gestiona automáticamente la transacción:
- Si el bloque termina bien ➡️ hace
commit(). - Si hay un error ➡️ hace
rollback()(deshace los cambios).
with sqlite3.connect("banco.db") as conn:
# Si algo falla dentro de este bloque, no se guarda nada
conn.execute("UPDATE cuentas SET saldo = saldo - 100 WHERE id = 1")
conn.execute("UPDATE cuentas SET saldo = saldo + 100 WHERE id = 2")
5️⃣ Configuración Avanzada: Row Factory
Por defecto, SQLite devuelve los resultados como tuplas. Esto es poco práctico porque tienes que recordar que el nombre es la posición [1].
Podemos hacer que se comporte como un diccionario usando sqlite3.Row:
conexion = sqlite3.connect("datos.db")
conexion.row_factory = sqlite3.Row # ¡Configuración mágica!
cursor = conexion.cursor()
cursor.execute("SELECT * FROM usuarios")
fila = cursor.fetchone()
print(fila["nombre"]) # ¡Mucho más legible que fila[1]!
✅ Buenas prácticas con SQLite
- Usa
with: Para gestionar transacciones de forma segura. - Cierra siempre: Aunque el
withgestione la transacción, elclose()libera el archivo del SO. - Mayúsculas en SQL: Escribe las palabras reservadas (
SELECT,FROM,WHERE) en mayúsculas para diferenciar el código. - No insertes variables directamente: Nunca uses f-strings para meter datos en SQL (riesgo de Inyección SQL). Usa marcadores
?(lo veremos en el siguiente punto).
🧪 Ejercicios prácticos – Misión: La Cripta de Datos
Has sido contratado para digitalizar el inventario de una antigua biblioteca secreta que ha decidido abandonar los pergaminos de texto y pasarse a la persistencia digital.
🟢 Fase 1 – Cimentación del Sistema
🟩 Ejercicio 1 – La Primera Piedra
Crea un script que genere una base de datos llamada biblioteca_secreta.db. Si la base de datos ya existe, simplemente debe conectar. Al finalizar, debe imprimir un mensaje confirmando la conexión y cerrar el recurso correctamente.
🟦 Ejercicio 2 – Estructura del Conocimiento
Define una tabla llamada libros con los siguientes requisitos:
id: Entero, clave primaria y autoincremental.titulo: Texto, obligatorio (NOT NULL).autor: Texto, obligatorio.paginas: Entero, debe ser mayor que 0 (CHECK).estado: Texto, por defecto valor 'disponible'.
🟡 Fase 2 – Integridad y Memoria
🟨 Ejercicio 3 – El Laboratorio de Pruebas
Crea una base de datos en memoria (:memory:).
- Crea la misma tabla
librosdel ejercicio anterior. - Intenta crear una tabla
alquimiacon un solo campoid. - Consulta la tabla especial de sistema de SQLite (
sqlite_master) para listar todas las tablas creadas y muéstralas por pantalla.
🟧 Ejercicio 4 – Transacción Riesgosa
Simula el siguiente escenario:
- Abre una conexión a
biblioteca_secreta.db. - Crea una tabla
socios(id,nombre). - Inicia un bloque
with(transacción). - Inserta un socio.
- Fuerza un error (por ejemplo, dividiendo por cero o lanzando una excepción manual).
- Tras el error, verifica si el socio se guardó. Si has usado bien el
with, la base de datos debería estar vacía de socios.
🔴 Fase 3 – Configuración Experta
🟥 Ejercicio 5 – El Bibliotecario Eficiente
Configura una conexión a biblioteca_secreta.db para que use sqlite3.Row.
- Crea una tabla
configuracioncon los camposclave(TEXT) yvalor(TEXT). - Inserta manualmente (usando el cursor) la fila
('idioma', 'español'). - Haz un
SELECT *y accede al valor de 'idioma' usando la clave del diccionario, no el índice numérico.